﻿#include "CStrUtils.h"
#include <tchar.h>
#include <algorithm>
#include <locale>

template<typename charT>
struct TCharCmpEqual {
    explicit TCharCmpEqual(const std::locale& loc) : loc_(loc) {}
    bool operator()(charT ch1, charT ch2) {
        return std::toupper(ch1, loc_) == std::toupper(ch2, loc_);
    }
private:
    const std::locale& loc_;
};

template<typename T>
size_t CStrUtils::FindSubString(const T& str1, const T& str2, const std::locale& loc/* = std::locale()*/)
{
    typename T::const_iterator it = std::search(str1.begin(), str1.end(),
        str2.begin(), str2.end(), TCharCmpEqual<typename T::value_type>(loc));
    if (it != str1.end())
    {
        return it - str1.begin();
    }

    return _tstring::npos;
}

_tstring CStrUtils::Format(LPCTSTR formatstring, ...)
{
    _tstring strResult;
    va_list args;
    LPTSTR lpBuf = nullptr;
    DWORD dwCchCount = MAX_PATH;

    va_start(args, formatstring);

    do
    {
        //分配缓冲
        lpBuf = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwCchCount * sizeof(TCHAR));
        if (nullptr == lpBuf)
        {
            break;
        }

        //成功则赋值字符串并终止循环
        if (-1 != _vsntprintf_s(lpBuf, dwCchCount, _TRUNCATE, formatstring, args))
        {
            strResult = lpBuf;
            break;
        }

        //释放缓冲, 待下次重新分配
        ::HeapFree(::GetProcessHeap(), 0, lpBuf);
        lpBuf = nullptr;

        //缓冲字符数在合理范围内则扩大2倍
        if (dwCchCount < INT32_MAX)
        {
            dwCchCount *= 2;
        }
        else
        {
            //超限终止处理
            break;
        }

    } while (true);

    //释放缓冲
    if (nullptr != lpBuf)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpBuf);
        lpBuf = nullptr;
    }

    va_end(args);

    return strResult;
}

std::vector<_tstring> CStrUtils::SplitStr(const _tstring& str, const _tstring& delim)
{
    std::vector<_tstring> vectorOut;
    size_t iStart = 0;
    size_t iEnd = 0;

    while ((iStart = str.find_first_not_of(delim, iEnd)) != _tstring::npos)
    {
        iEnd = str.find(delim, iStart);
        vectorOut.push_back(str.substr(iStart, iEnd - iStart));
    }

    return vectorOut;
}

_tstring& CStrUtils::Replace(_tstring& strSrc, const _tstring& strFind, const _tstring& strReplace, bool bCase/* = false*/)
{
    _tstring strDest = strSrc;
    size_t nFind = strDest.find(strFind);

    if (bCase)
    {
        do
        {
            nFind = strDest.find(strFind);
            if (_tstring::npos != nFind)
            {
                strDest.replace(nFind, strFind.size(), strReplace);
            }

        } while (_tstring::npos != nFind);
    }
    else
    {
        do
        {
            nFind = FindSubString(strDest, strFind);
            if (_tstring::npos != nFind)
            {
                strDest.replace(nFind, strFind.size(), strReplace);
            }

        } while (_tstring::npos != nFind);
    }

    strSrc = strDest;
    return strSrc;
}

int CStrUtils::Compare(LPCTSTR pstrSrc, LPCTSTR pstrDest, bool bCase/* = true*/)
{
    if (bCase)
    {
        return _tcscmp(pstrSrc, pstrDest);
    }

    return _tcsicmp(pstrSrc, pstrDest);
}

int CStrUtils::Compare(const _tstring& strSrc, const _tstring& strDest, bool/* bCase = true*/)
{
    return Compare(strSrc.c_str(), strDest.c_str());
}

std::string CStrUtils::_WStrToOtherStr(UINT CodePage, const std::wstring& str)
{
    std::string strResult;
    LPSTR lpMultiByteStr = NULL;

    do
    {
        int nConverted = 0;

        //计算缓冲区所需的字节大小
        nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
        if (0 == nConverted)
        {
            break;
        }

        //分配内存
        lpMultiByteStr = (LPSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nConverted);
        if (NULL == lpMultiByteStr)
        {
            break;
        }

        //转换
        nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, lpMultiByteStr, nConverted, NULL, NULL);
        if (0 == nConverted)
        {
            break;
        }

        strResult = lpMultiByteStr;

    } while (false);

    //释放缓冲
    if (NULL != lpMultiByteStr)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpMultiByteStr);
        lpMultiByteStr = NULL;
    }

    return strResult;
}

std::wstring CStrUtils::_OtherStrToWStr(UINT CodePage, const std::string& str)
{
    std::wstring strResult;
    LPWSTR lpWideStr = NULL;

    do
    {
        int nConverted = 0;

        //计算缓冲区所需的字节大小
        nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, 0);
        if (0 == nConverted)
        {
            break;
        }

        //分配缓冲内存
        lpWideStr = (LPWSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nConverted * sizeof(WCHAR));
        if (NULL == lpWideStr)
        {
            break;
        }

        //转换字符串
        nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, lpWideStr, nConverted);
        if (0 == nConverted)
        {
            break;
        }

        strResult = lpWideStr;

    } while (false);

    //释放缓冲
    if (NULL != lpWideStr)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpWideStr);
        lpWideStr = NULL;
    }

    return strResult;
}

_tstring CStrUtils::WStrToTStr(const std::wstring& str)
{
#ifdef _UNICODE
    return str;
#else
    return _WStrToOtherStr(CP_ACP, str);
#endif
}

_tstring CStrUtils::AStrToTStr(const std::string& str)
{
#ifdef _UNICODE
    return _OtherStrToWStr(CP_ACP, str);
#else
    return str;
#endif
}

_tstring CStrUtils::U8StrToTStr(const std::string& str)
{
#ifdef _UNICODE
    return _OtherStrToWStr(CP_UTF8, str);
#else
    return _WStrToOtherStr(CP_ACP, _OtherStrToWStr(CP_UTF8, str));
#endif
}

std::wstring CStrUtils::TStrToWStr(const _tstring& str)
{
#ifdef _UNICODE
    return str;
#else
    return _OtherStrToWStr(CP_ACP, str);
#endif
}

std::string CStrUtils::TStrToAStr(const _tstring& str)
{
#ifdef _UNICODE
    return _WStrToOtherStr(CP_ACP, str);
#else
    return str;
#endif
}

std::string CStrUtils::TStrToU8Str(const _tstring& str)
{
#ifdef _UNICODE
    return _WStrToOtherStr(CP_UTF8, str);
#else
    return _WStrToOtherStr(CP_UTF8, _OtherStrToWStr(CP_ACP, str));
#endif
}

std::string CStrUtils::WStrToAStr(const std::wstring& str)
{
    return _WStrToOtherStr(CP_ACP, str);
}

std::string CStrUtils::WStrToU8Str(const std::wstring& str)
{
    return _WStrToOtherStr(CP_UTF8, str);
}

std::wstring CStrUtils::AStrToWStr(const std::string& str)
{
    return _OtherStrToWStr(CP_ACP, str);
}

std::string CStrUtils::AStrToU8Str(const std::string& str)
{
    return _WStrToOtherStr(CP_UTF8, _OtherStrToWStr(CP_ACP, str));
}

std::wstring CStrUtils::U8StrToWStr(const std::string& str)
{
    return _OtherStrToWStr(CP_UTF8, str);
}

std::string CStrUtils::U8StrToAStr(const std::string& str)
{
    return _WStrToOtherStr(CP_ACP, _OtherStrToWStr(CP_UTF8, str));
}